/*
 * Copyright Hundsun Technologies Inc. All Rights Reserved.
 */

package fifo

import (
	"container/list"
	"sync"
)

// FIFO fifo queue
type FIFO struct {
	m    map[interface{}]*list.Element
	l    *list.List
	size int
	lock sync.RWMutex
}

type entry struct {
	key   interface{}
	value interface{}
}

// NewFIFO  new fifo queue
func NewFIFO(size int) *FIFO {
	if size <= 0 {
		size = 1
	}
	return &FIFO{
		m:    make(map[interface{}]*list.Element, size),
		l:    list.New(),
		size: size,
	}
}

// Contains check is a key is in the cache
func (fi *FIFO) Contains(key interface{}) bool {
	fi.lock.RLock()
	defer fi.lock.RUnlock()
	_, ok := fi.m[key]
	return ok
}

//Get get form fifo
func (fi *FIFO) Get(key interface{}) (value interface{}, ok bool) {
	fi.lock.RLock()
	defer fi.lock.RUnlock()

	if elem, ok := fi.m[key]; ok {
		return elem.Value.(*entry).value, true
	}
	return
}

//Add add to fifo
func (fi *FIFO) Add(key, value interface{}) {
	fi.lock.Lock()
	defer fi.lock.Unlock()

	if fi.l.Len() >= fi.size {
		ent := fi.l.Remove(fi.l.Front()).(*entry)
		delete(fi.m, ent.key)
	}

	ent := &entry{key, value}
	elem := fi.l.PushBack(ent)
	fi.m[key] = elem
}

//Remove remove from fifo
func (fi *FIFO) Remove(key interface{}) (present bool) {
	fi.lock.Lock()
	defer fi.lock.Unlock()

	if elem, ok := fi.m[key]; ok {
		ent := fi.l.Remove(elem).(*entry)
		delete(fi.m, ent.key)
		return true
	}
	return false
}
